/***天地图缓冲工具类，对外开放。
 * 允许用户在地图上拖拽完成地理空间目标（点、线、面、圆、多边形）的缓冲，
 * 用于表达地理空间目标的影响范围或服务范围等信息。
 * 使用者可以自定义缓冲面的相关样式，例如缓冲面的填充颜色、透明度等等。
 * 注：chrome、safari、IE9及以上浏览器，缓冲算法函数依赖turf.js。
 *@author juyang
 * ***/

T.BufferTool = T.Tool.extend({
    options: {
        style: {
            color: "red", weight: 3, opacity: 0.5, fillColor: "#FFFFFF", fillOpacity: 0.5, lineStyle: 'dashed'
        }
    },


    /**
     * 构造
     * @param map 地图对象
     * @param layers 被缓冲的图层
     * @param options
     */
    initialize: function (map, layers, options) {
        T.setOptions(this, options);
        this._map = map;
        this.OverlayGroup = layers;
        this.buffers = new T.LayerGroup();
        this._map.addLayer(this.buffers);
        //兼容移动端，H5吗
        var ua = navigator.userAgent;
        var ie = ua.match(/MSIE\s([\d.]+)/)
        var edge = ua.match(/Edge\/([\d.]+)/);
        var touch='ontouchstart' in window && !ie && !edge
        this.mousemove = touch ? "touchmove" :
            "mousemove";
        this.mouseup = touch ? "touchend" :
            "mouseup";
    },

    enable: function () {
        if (this._enabled) { return; }

        this._enabled = true;
        this.addHooks();
    },

    disable: function () {
        if (!this._enabled) { return; }

        this._enabled = false;
        this.removeHooks();
    },

    /**
     * 打开工具
     */
    open: function () {
        this.enable();
    },

    /**
     * 关闭工具
     */
    close: function () {
        this.disable();
    },

    addHooks: function () {


        if (this._map) {
            /**
             * 在要拖拽的图层注册mousedown，地图注册mousemove和mouseup
             * **/
            for (var k = 0; k < this.OverlayGroup.length; k++) {
                var layer = this.OverlayGroup[k];
                layer.on('mousedown', this._onLayerDragStart, this);
                if (layer.buffer) {
                    layer.buffer.on('mousedown', this._onbufferLayerDragStart, this);
                }
            }
            this._map.on(this.mousemove, this._onLayerDrag, this);
            this._map.on(this.mouseup, this._onLayerDragEnd, this);
        }

    },
    /**
     * 析构函数，移除所有事件的注册，
     */
    removeHooks: function () {
        if (this._map) {
            this._map.off(this.mousemove, this._onLayerDrag, this);
            this._map.off(this.mouseup, this._onLayerDragEnd, this);
            for (var k = 0; k < this.OverlayGroup.length; k++) {
                var layer = this.OverlayGroup[k];
                layer.off('mousedown', this._onLayerDragStart, this);
                if (layer.buffer) {
                    layer.buffer.off('mousedown', this._onbufferLayerDragStart, this);
                }
            }
        }
    },

    /**
     * 清除缓冲区图层
     */
    clear: function () {
        for (var k = 0; k < this.OverlayGroup.length; k++) {
            var layer = this.OverlayGroup[k];
            if (layer.buffer) {
                layer.buffer.off('mousedown', this._onbufferLayerDragStart, this);
                delete  layer.buffer;
            }
        }
        this.buffers.clearLayers();
    },

    /**
     * 增加一个被缓冲的图层
     * @param layer
     */
    addLayer: function (layer) {
        this.OverlayGroup.push(layer);
        layer.on('mousedown', this._onLayerDragStart, this);
        if (layer.buffer) {
            layer.buffer.on('mousedown', this._onbufferLayerDragStart, this);
        }
    },
    /**
     * 获取所有缓冲图层
     * @returns {*}
     */
    getBufferLayers: function () {
        return this.buffers.getLayers();
    },

    /**
     * 图层拖拽
     * @param e
     * @private
     */
    _onLayerDragStart: function (e) {
        var layer = e.layer || e.target || e;
        if (layer.buffer) {
            this.dragingLayer = layer.buffer;
        }
        else {
            var bufferlayer;
            if (layer.getLngLats) {//线段、多边形
                var newGeo = this._buffer(layer.toGeoJSON(), 0.001);
                bufferlayer = new T.Polygon(this.geoJsonToLngLats(newGeo), this.options.style);
            }
            else if (layer.getLngLat || layer.getCenter) { //点和圆
                var Radius = layer.getRadius ? layer.getRadius() + 1 : 1;
                var lnglat = layer.getCenter ? layer.getCenter() : layer.getLngLat();
                bufferlayer = new T.Circle(lnglat, Radius, this.options.style);
            }
            //缓冲图层和原图层建立引用关系。
            bufferlayer.layer = layer;
            this.dragingLayer = layer.buffer = bufferlayer;
            this.dragingLayer.on('mousedown', this._onbufferLayerDragStart, this);
            this.trigger('dragStart', {layer: layer, lnglat: e.lnglat});
        }
        this.buffers.addLayer(this.dragingLayer);
        this._map.disableDrag();//暂时停止拖拽。
        this.dragingLayer.start_distance = 0;
        this.dragingLayer.distance = 0;
    },

    /**
     * 缓冲图层的重新拖拽开始的注册函数
     * @param e
     * @private
     */
    _onbufferLayerDragStart: function (e) {
        var buffer = e.layer || e.target || e;
        var layer = buffer.layer;
        this.dragingLayer = buffer;
        this.trigger('dragStart', {layer: layer, lnglat: e.lnglat})
        this._map.disableDrag();
        this.dragingLayer.start_distance = this.dragingLayer.distance;


    },

    /**
     * 拖拽结束时的注册函数
     * @param e
     * @private
     */
    _onLayerDragEnd: function (e) {
        if (!this.dragingLayer) return;
        var buffer = this.dragingLayer;
        var layer = buffer.layer;
        if (!buffer) return;
        if ((layer.getRadius && this.dragingLayer.distance - layer.getRadius() > 10 )
            || (!layer.getRadius && this.dragingLayer.distance > 5)) {
            //注册事件
            var layer = buffer.layer;
            this.trigger("dragEnd", {
                layer: layer,
                buffer: layer.buffer,
                lnglat: e.lnglat,
                radius: this.dragingLayer.distance
            });
        }
        else { //缓冲图层太小，舍弃。
            this.buffers.removeLayer(buffer);
            delete  buffer;
        }

        this.dragingLayer = null;
        this._map.enableDrag();
    },

    /**
     * 拖拽中触发的函数
     * @param e
     * @private
     */
    _onLayerDrag: function (e) {
        if (!this.dragingLayer) return;
        var layer = this.dragingLayer.layer;
        this.dragingLayer.drag_distance = this._getDistancebyLayer(layer, e.lnglat);
        var distancet = this.dragingLayer.drag_distance - this.dragingLayer.start_distance;
        this.dragingLayer.distance = this.dragingLayer.distance + distancet;
        this.dragingLayer.start_distance = this.dragingLayer.distance;

        if (layer.getLngLats) {
            var newGeo = this._buffer(layer.toGeoJSON(), this.dragingLayer.distance);
            this.dragingLayer.setLngLats(this.geoJsonToLngLats(newGeo));
        }
        else if (layer.getLngLat || layer.getCenter()) {
            var Radius = layer.getRadius ? layer.getRadius() : 0;
            this.dragingLayer.setRadius(this.dragingLayer.distance > Radius ? this.dragingLayer.distance : Radius);
        }
        //注册事件
        this.trigger("drag", {layer: layer, buffer: layer.buffer, lnglat: e.lnglat, radius: this.dragingLayer.distance});
    },
    /**
     * 计算点到图层的最短距离
     * @param layer
     * @param elnglat
     * @returns {number}
     * @private
     */
    _getDistancebyLayer: function (layer, elnglat) {
        var distance = 0;
        if (layer.getLngLats) {  //线段、多边形
            var lnglats = layer instanceof T.Polygon ?
                layer.getLngLats()[0] : layer.getLngLats();
            distance = this._getDistanceByLnglat(elnglat, lnglats, !!(layer instanceof T.Polygon));
            if (!!(layer instanceof T.Polygon)) {
                var inside = this._inside(elnglat, layer.toGeoJSON());
                if (inside)distance = 0;
            }
        }
        else if (layer.getLngLat || layer.getCenter()) {
            var Radius = layer.getRadius ? layer.getRadius() : 0;
            var lnglat = layer.getCenter ? layer.getCenter() : layer.getLngLat();
            distance = this._map.getDistance(lnglat, elnglat);
            if (Radius >= distance) distance = 0;

        }

        return distance;
    },

    /**
     * 计算点到图层的最短距离
     * @param elnglat 点坐标
     * @param lnglats 图层点的所有坐标
     * @param isPolygon  是否是面图层？
     * @returns {*}
     * @private
     */
    _getDistanceByLnglat: function (elnglat, lnglats, isPolygon) {
        if (lnglats.length < 2) return 0;
        var min = null;
        var _lnglats = lnglats.slice(0)
        if (isPolygon)_lnglats.push(new T.LngLat(lnglats[0].lng, lnglats[0].lat));
        for (var k = 0; k < _lnglats.length - 1; k++) {
            var p0 = this._map.lngLatToLayerPoint(elnglat)
            var p1 = this._map.lngLatToLayerPoint(_lnglats[k])
            var p2 = this._map.lngLatToLayerPoint(_lnglats[k + 1])
            var p = this._sqClosestPointOnSegment(p0, p1, p2);
            var _lnglat = this._map.layerPointToLngLat(p)
            var d = this._map.getDistance(_lnglat, elnglat)
            min = min != null ? Math.min(min, d) : d;

        }
        return min;

    },

    /**
     * 计算像素点到另一两点组成线段的最短像素距离所在点。
     * @param p 目标点
     * @param p1 线段起始点
     * @param p2 线段终点
     * @param sqDist
     * @returns {*}
     * @private
     */
    _sqClosestPointOnSegment: function (p, p1, p2, sqDist) {

        var x = p1.x,
            y = p1.y,
            dx = p2.x - x,
            dy = p2.y - y,
            dot = dx * dx + dy * dy,
            t;

        if (dot > 0) {
            t = ((p.x - x) * dx + (p.y - y) * dy) / dot;

            if (t > 1) {
                x = p2.x;
                y = p2.y;
            } else if (t > 0) {
                x += dx * t;
                y += dy * t;
            }
        }

        dx = p.x - x;
        dy = p.y - y;
        return sqDist ? dx * dx + dy * dy : new T.Point(x, y);
    },


    /**
     * 坐标点和多边形的位置关系
     * @param lnglat 坐标点
     * @param poly 多边形
     * @returns {*}
     * @private
     */
    _inside: function (lnglat, poly) {
        var pt1 = turf.point([lnglat.lng, lnglat.lat]);
        var isInside1 = turf.inside(pt1, poly);
        return isInside1;
    },

    _buffer: function (geoJSON, radius) {
        var unit = 'kilometers';

        var buffered = turf.buffer(geoJSON, radius / 1000, unit);
        return buffered;
    },


    //提取坐标
    geoJsonToLngLats: function (newGeo) {
        var LngLats = [];
        for (var i = 0; i < newGeo.geometry.coordinates.length; i++) {
            var cos = newGeo.geometry.coordinates[i];
            for (var j = 0; j < cos.length; j++) {
                var geoJson = cos[j];
                LngLats.push(new T.LngLat(geoJson[0], geoJson[1]));
            }
        }
        return LngLats;
    },
})